package link.luyu.demo.port;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.List;
import javax.annotation.Resource;
import link.luyu.demo.port.config.ApplicationConfig;
import link.luyu.demo.port.db.LuyuHashLockTableBean;
import link.luyu.protocol.network.ECDSALuyuSign;
import link.luyu.protocol.network.LuyuAccount;
import link.luyu.sdk.caller.LuyuSDK;
import link.luyu.sdk.caller.SDKFactory;
import link.luyu.sdk.exception.LuyuSDKException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties
public class Application implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        try {
            SpringApplication.run(Application.class, args);
        } catch (Exception e) {
            logger.error("Application.main exception: ", e);
            System.exit(-1);
        }
    }

    @Resource ApplicationConfig ac;
    @Resource HashLockManager hlm;

    private void as_a_lock(Contract contract) {
        logger.info("------ working as a lock ------");

        String[] ids = contract.getExportRequestCargoIds();
        for (String cargoId : ids) { // cargoId
            LuyuHashLockTableBean bean = hlm.getBeanByCargoId(cargoId);
            if (bean != null) {
                logger.info("A_Lock already exist cargoId: " + cargoId);
                continue;
            }

            String[] res = contract.getExportRequestByCargoId(cargoId);
            if (res == null) {
                logger.info("failed to getExportRequestByCargoId:" + cargoId);
                continue;
            }
            String toChainPath = res[0];
            String receiver = res[1];

            String cargoURI = contract.getCargoURI(cargoId);
            if (cargoURI == null) {
                logger.info("failed to getCargoURI:" + cargoURI);
                continue;
            }

            BigInteger secretBI = new BigInteger(256, new SecureRandom());
            String secret = secretBI.toString(16);
            String hashLock = Tool.sha256(secret);
            String lockTime = "" + ((System.currentTimeMillis() / 1000) + 3600 * 10);
            String txHash =
                    contract.transferPropose(
                            ac.getChain().getLuyuIdentity(),
                            cargoId, // cargoId
                            cargoURI, // cargoURI
                            ac.getChain().getPath(),
                            toChainPath,
                            receiver,
                            hashLock,
                            lockTime);

            if (txHash == null || txHash.length() == 0) {
                logger.info("failed to portTransferPropose, try again later...");
                continue;
            }

            logger.info("transfer propose txhash:" + txHash);

            bean = new LuyuHashLockTableBean();
            bean.setCargoId(cargoId);
            bean.setFromChainPath(ac.getChain().getPath());
            bean.setToChainPath(res[0]);
            bean.setSecret(secret);
            bean.setHashLock(hashLock);
            bean.setLockTime(lockTime);
            bean.setStatus("A_Lock");
            hlm.saveBean(bean);
        }

        logger.info("------ end of working as a lock ------");
    }

    public void as_b_lock(Contract contract) {
        logger.info("------ working as b lock ------");

        List<ApplicationConfig.Resource> resources = ac.getChain().getPeerResources();
        for (ApplicationConfig.Resource peer : resources) {
            logger.info("to check peer path: " + peer.asPath());

            String[] ids = contract.getTransferingCargoIds(peer.asPath());
            for (String id : ids) {
                LuyuHashLockTableBean bean = hlm.getBeanByCargoId(id);
                if (bean != null) {
                    logger.info("B_Lock already exist cargoId: " + id);
                    continue;
                }

                Contract.CargoInfo ci = contract.getCargoInfo(peer.asPath(), id);
                if (ci == null) {
                    logger.info("contract query not found: " + id);
                    continue;
                }

                if (!ci.toChainPath.equals(ac.getChain().getPath())) {
                    logger.info("unknown chain path: " + ci.toChainPath);
                    continue;
                }

                String lockTime = "" + (ci.lockTime - 3600 * 4);
                String txHash =
                        contract.transferPropose(
                                ac.getChain().getLuyuIdentity(),
                                id, // cargoId
                                ci.cargoURI, // cargoURI
                                ci.fromChainPath,
                                ci.toChainPath,
                                ci.receiver,
                                ci.hashLock,
                                lockTime);
                if (txHash == null || txHash.length() == 0) {
                    logger.info("failed to portTransferPropose, try again later...");
                    continue;
                }

                logger.info("transfer propose txhash:" + txHash);

                bean = new LuyuHashLockTableBean();
                bean.setCargoId(id);
                bean.setFromChainPath(ci.fromChainPath);
                bean.setToChainPath(ci.toChainPath);
                bean.setHashLock(ci.hashLock);
                bean.setLockTime(lockTime);
                bean.setStatus("B_Lock");
                hlm.saveBean(bean);
            }
        }

        logger.info("------ end of working as b lock ------");
    }

    public void as_a_unlock(Contract contract) {
        logger.info("------ working as a unlock ------");

        List<LuyuHashLockTableBean> beans = hlm.getMyExportCargo(ac.getChain().getPath(), "A_Lock");
        for (LuyuHashLockTableBean bean : beans) {
            ApplicationConfig.Resource peer = null;
            for (ApplicationConfig.Resource p : ac.getChain().getPeerResources()) {
                if (p.chainPath.equals(bean.getToChainPath())) {
                    peer = p;
                    break;
                }
            }
            if (peer == null) {
                logger.info("peer toChainPath not found for bean: " + bean);
                continue;
            }

            Contract.CargoInfo ci = contract.getCargoInfo(peer.asPath(), bean.getCargoId());
            if (ci == null) {
                logger.info("contract query not found: " + bean.getCargoId());
                continue;
            }

            if (!ci.fromChainPath.equals(ac.getChain().getPath())) {
                logger.info("un-match chain path: " + ci.fromChainPath);
                continue;
            }

            if (!ci.cargoStatus.equals("transfering")) {
                logger.info("un-match status: " + ci.cargoStatus);
                continue;
            }

            if (!ci.hashLock.equals(bean.getHashLock())) {
                logger.info("un-match hashLock: " + ci);
                continue;
            }

            logger.info("to unlock peer: " + peer.asPath() + " secret: " + bean.getSecret());
            String txHash =
                    contract.transferConfirm(
                            ac.getChain().getLuyuIdentity(),
                            peer.asPath(),
                            bean.getCargoId(),
                            bean.getSecret());
            if (txHash == null || txHash.length() == 0) {
                logger.info("failed to portTransferPropose, try again later...");
                continue;
            }

            logger.info("transfer confirm txhash:" + txHash);

            bean.setStatus("A_Unlock");
            hlm.saveBean(bean);
        }

        logger.info("------ end of working as a unlock ------");
    }

    public void as_b_unlock(Contract contract) {
        logger.info("------ working as b unlock ------");

        List<LuyuHashLockTableBean> beans = hlm.getMyImportCargo(ac.getChain().getPath(), "B_Lock");
        for (LuyuHashLockTableBean bean : beans) {
            Contract.CargoInfo ci =
                    contract.getCargoInfo(
                            ac.getChain().getPath() + "." + ac.getChain().getPortContract(),
                            bean.getCargoId());
            if (ci == null) {
                logger.info("contract query not found: " + bean.getCargoId());
                continue;
            }

            if (!ci.toChainPath.equals(ac.getChain().getPath())) {
                logger.info("un-match chain path: " + ci.toChainPath);
                continue;
            }

            if (ci.secret == null || ci.secret.equals("")) {
                logger.info("waiting to unlock: " + ci);
                continue;
            }

            ApplicationConfig.Resource peer = null;
            for (ApplicationConfig.Resource p : ac.getChain().getPeerResources()) {
                if (p.chainPath.equals(ci.fromChainPath)) {
                    peer = p;
                    break;
                }
            }

            if (peer == null) {
                logger.info("un-match chain path: " + ci.fromChainPath);
                continue;
            }

            logger.info("to unlock peer: " + peer.asPath() + " secret: " + ci.secret);
            String txHash =
                    contract.transferConfirm(
                            ac.getChain().getLuyuIdentity(),
                            peer.asPath(),
                            bean.getCargoId(),
                            ci.secret);

            if (txHash == null || txHash.length() == 0) {
                logger.info("failed to portTransferPropose, try again later...");
                continue;
            }

            logger.info("transfer confirm txhash:" + txHash);

            bean.setStatus("B_Unlock");
            bean.setSecret(ci.secret);
            hlm.saveBean(bean);
        }

        logger.info("------ end of working as b unlock ------");
    }

    @Override
    public void run(String... args) {
        System.out.println("Port-Service start success");
        logger.info("" + ac);

        try {
            LuyuAccount luyuAccount =
                    LuyuAccount.build(
                            new ECDSALuyuSign(),
                            ac.getChain().getLuyuPubKeyAsBytes(),
                            ac.getChain().getLuyuSecKeyAsBytes());
            luyuAccount.setIdentity(ac.getChain().getLuyuIdentity());
            LuyuSDK sdk = SDKFactory.build(luyuAccount);

            String supplyContractPath =
                    ac.getChain().getPath() + "." + ac.getChain().getSupplyContract();
            String portContractPath =
                    ac.getChain().getPath() + "." + ac.getChain().getPortContract();

            Contract contract = new Contract(sdk, supplyContractPath, portContractPath);

            while (true) {
                as_a_lock(contract);
                as_b_lock(contract);
                as_a_unlock(contract);
                as_b_unlock(contract);

                Thread.sleep(10000);
            }
        } catch (LuyuSDKException e) {
            logger.error("SDKFactory build error: " + e);
            return;
        } catch (InterruptedException ie) {
            logger.error("interrupted, exit...");
        }
    }
}
